package sma.iss;

import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Locale;

import sma.iss.dto.RotState;
import sma.iss.util.AngleUtil;

/**
 * Position and speed of SAWs.<br>
 * 2 + 8 x (angles + speeds) that discribe the position of ISS SAWs at one time
 * 
 * @author smarsoll
 * 
 */
public class Position {

	/** Unset score. */
	public static final double UNSET = Double.NEGATIVE_INFINITY;

	/** temporary value for dynamic model. */
	public double shift = 0.0;

	/** temporary value for dynamic model. */
	public int minute = -7000;

	public final int getMinute() {
		if (minute == -7000) {
			throw new RuntimeException("minute not set");
		}
		return minute;
	}

	public final void setMinute(int minute) {
		this.minute = minute;
	}

	public int generation = 0;

	public enum Origin {
		DEFAULT, PROJECTION, MAX, RANDOM, MID2, MID8, ZERO, CHILD, CHILDPROJ, CHILDMAX, MUTANT, INTERPOL, CSV;
	}

	public Origin origin = Origin.DEFAULT;

	/** Position before this one. */
	public Position parent;
	/** for calculation with advance */
	public Position childAdvance;

	/** angles and speeds. */
	private double[] state = new double[20];
	private RotState[] rotes = new RotState[10];
	
	/** keep tracks of dangers. */
	public int[] dangerCounter = new int[8];
	
	public final void setInterpolated(int numRot, boolean interpolated) {
		rotes[numRot].isInterpolated = interpolated;
	}

	public final boolean isInterpolated(int numRot) {
		try {
			return rotes[numRot].isInterpolated;
		} catch (Exception e) {
			throw new RuntimeException("numRot=" + numRot, e);
		}
	}

	private double score = UNSET;
	private double cumulatedScore = 0.0;
	private boolean cumulated = false;

	public Boolean transitionOk = null;

	/** For genetic selection of scores that have position far from best score . */
	public double tempDistance = UNSET;
	/** Specific to Yaw model. */
	public double tempYaw = UNSET;


	public Position() {
		super();
		for (int numRot = 0; numRot < 10; numRot++) {
			rotes[numRot] = new RotState();
		}
		Arrays.fill(dangerCounter, 0);
		// for (int i = 0; i < 8; i++) {
		// dangerCounter[i] = 0;
		// }
	}

	/** Copy initial position */
	public Position(Position posModel) {
		this();
		System.arraycopy(posModel.state, 0, this.state, 0, 20);
		this.minute = posModel.minute;
		this.parent = posModel.parent;
		this.cumulatedScore = posModel.cumulatedScore;
		this.tempYaw = posModel.tempYaw;
		if (posModel.childAdvance != null) {
			this.childAdvance = new Position(posModel.childAdvance);
		}
	}

	public Position(Position posModel, Origin initOrigin, int initGen) {
		this(posModel);
		this.origin = initOrigin;
		this.generation = initGen;
	}

	public final double[] getState() {
		return state;
	}

	public final double getAngle(int numRot) {
		return state[numRot * 2];
	}

	public final void setAngle(int numRot, double angle) {
		state[numRot * 2] = angle;
	}

	public final double getSpeed(int numRot) {
		return state[numRot * 2 + 1];
	}

	public final void setSpeed(int numRot, double speed) {
		state[numRot * 2 + 1] = speed;
	}

	public final void setState(double[] state) {
		assert state.length == 20;
		this.state = state;
	}

	/** assumption we do not change speeds (no acceleration). */
	public void initState(Position posBefore, int deltaMin, int nbChildren) {
		this.parent = posBefore;
		// double[] stateBefore = posBefore.getState();
		// iterate on 10*2 positions
		for (int numRot = 0; numRot < 10; numRot++) {
			// copy speed
			double speed = posBefore.getSpeed(numRot);
			setSpeed(numRot, speed);
			// calculate new position
			// state[2 * numRot] = AngleUtil.reduce(stateBefore[2 * numRot] + deltaSec * speed);
			double newAngle = AngleUtil.addAngle(posBefore.getAngle(numRot), deltaMin * 60.0 * speed);
			setAngle(numRot, newAngle);
			if (newAngle < 0.0) {
				throw new RuntimeException("newAngle=" + newAngle);
			}
		}
		if (nbChildren > 0) {
			Position child = new Position();
			this.childAdvance = child;
			child.origin = Origin.CHILDPROJ;
			child.setMinute(this.getMinute() + deltaMin);
			child.initState(this, deltaMin, nbChildren - 1);
		}

	}

	public final double getAbsSpeed() {
		double absSpeed = 0.0;
		// for (int numRot = 0; numRot < 2; numRot++) {
		// // speed of SARJ count for 4 SAWs
		// absSpeed += Math.abs(4.0 * getSpeed(numRot));
		// }
		for (int numRot = 2; numRot < 10; numRot++) {
			// speed of SARJ count for 4 SAWs
			absSpeed += Math.abs(getSpeed(numRot));
		}
		return absSpeed;
	}

	public final double distance(Position pos2) {
		double distance = 0.0;
		for (int numRot = 0; numRot < 2; numRot++) {
			distance += Math.abs(AngleUtil.getShift(getAngle(numRot), pos2.getAngle(numRot)));
		}
		for (int numRot = 2; numRot < 10; numRot++) {
			distance += Math.abs(AngleUtil.getShift(getAngle(numRot), pos2.getAngle(numRot)));
			// also add speed differences
			distance += Math.abs(getSpeed(numRot) - pos2.getSpeed(numRot));
		}
		return distance;
	}

	public final double getScore() {
		return score;
	}

	public void setScore(double score) {
		this.score = score;
	}

	public final double getCumulatedScore() {
		if (!cumulated) {
			throw new RuntimeException("Score not yet cumulated");
		}
		return cumulatedScore;
	}

	public final void cumulateScore() {
		if (cumulated) {
			throw new RuntimeException("Cannot cumulate twice");
		}
		cumulated = true;
		cumulatedScore += score;
	}

	/** (beware of multi-threading). */
	private static NumberFormat FORMAT;
	private static NumberFormat FORMAT_SPEED;
			//new DecimalFormat("0.00");
	static {
		FORMAT = NumberFormat.getInstance(Locale.US);
		FORMAT.setMaximumFractionDigits(0);
		FORMAT_SPEED = NumberFormat.getInstance(Locale.US);
		FORMAT_SPEED.setMaximumFractionDigits(3);
		// FORMAT.getDecimalFormatSymbols().setDecimalSeparator('.');
	}

	@Override
	public String toString() {
		String nok = "";
		if (transitionOk == null) {
			nok = "ntr";
		} else if (transitionOk == false) {
			nok = "NOk";
		}
		String yawStr = "";
		if (tempYaw != UNSET) {
			yawStr = ", y" + tempYaw;
		}
		// removed "/" + FORMAT.format(cumulatedScore)
		return "Pos" + minute + "[sc=" + FORMAT.format(score) + nok + yawStr + ", s BGA="
				+ FORMAT_SPEED.format(this.getAbsSpeed()) + ", " + generation + "/"
				+ origin + "]";
	}

	public int getDelatMinuteToPos(Position nextPos) {
		int nextMinute = nextPos.getMinute();
		if (nextMinute < this.minute) {
			nextMinute += 92;
		}
		return nextMinute - this.minute;
	}

}
